using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
//using Newtonsoft.Json;

namespace secuerFTP
{
    public partial class Form2 : Form
    {
        ServerConfig serverConfig;
        public Form2(ServerConfig serverConfig)
        {
            InitializeComponent();
            this.serverConfig = serverConfig;
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        private void Form2_Load(object sender, EventArgs e)
        {
            Socket ListenSocket;
            try
            {
                //配置端口监听控制信息
                ListenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPAddress ListenIP = IPAddress.Any;
                IPEndPoint BindPoint = new IPEndPoint(ListenIP, serverConfig.GetServerPort());
                ListenSocket.Bind(BindPoint);
                ListenSocket.Listen(100);

                //配置端口监听数据信息
                Socket ListenDataTransportSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPAddress ListenIPData = IPAddress.Any;
                IPEndPoint ListenEndPoint = new IPEndPoint(ListenIPData, serverConfig.GetServerDataPort());
                ListenDataTransportSocket.Bind(ListenEndPoint);
                ListenDataTransportSocket.Listen(100);

                //创建监听线程开始监听
                ClientHandle handle = new ClientHandle();
                handle.CommandSocket = ListenSocket;
                handle.DataSocket = ListenDataTransportSocket;
                Thread ListenThread = new Thread(ListenForClient);
                ListenThread.IsBackground = true;
                ListenThread.Start(handle);
            }
            catch
            {
                MessageBox.Show("开启监听失败，请确保配置文件配置正确！", "服务启动出错", MessageBoxButtons.OK, MessageBoxIcon.Error);

            }


        }

        void ListenForClient(object obj)
        {
            ClientHandle handle = obj as ClientHandle;
            Socket ListenSocket = handle.CommandSocket;
            Socket ListenDataTransportSocket = handle.DataSocket;
            try
            {
                while (true)
                {
                    //接受控制连接
                    Socket ReceivedConnect = ListenSocket.Accept();
                    label1.Text = ReceivedConnect.RemoteEndPoint.ToString() + ": 控制tcp连接成功！";
                    ReceivedConnect.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); //设置KeepAlive保持连接
                    //ReceivedConnect.SetSocketOption(SocketOptionLevel.Socket, (SocketOptionName)16, 3); //16为TcpKeepAliveRetryCount即终止连接前心跳重发次数
                    //ReceivedConnect.SetSocketOption(SocketOptionLevel.Socket, (SocketOptionName)3, 3); //3为TcpKeepAliveTime即心跳包发送间隔
                                                                                                       //接收数据传输连接

                    Socket DataTransportSocket =  ListenDataTransportSocket.Accept();
                    label3.Text = DataTransportSocket.RemoteEndPoint.ToString() + "; 数据tcp连接成功！";
                    DataTransportSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); //设置KeepAlive保持连接
                    //DataTransportSocket.SetSocketOption(SocketOptionLevel.Socket, (SocketOptionName)16, 3); //16为TcpKeepAliveRetryCount即终止连接前心跳重发次数
                    //DataTransportSocket.SetSocketOption(SocketOptionLevel.Socket, (SocketOptionName)3, 3); //3为TcpKeepAliveTime即心跳包发送间隔

                    //开启新线程来保持与每个客户端连接
                    ClientHandle client = new ClientHandle();
                    client.CommandSocket = ReceivedConnect;
                    client.DataSocket = DataTransportSocket;
                    Thread ConnectSocket = new Thread(ReceiveCommand);
                    ConnectSocket.IsBackground = true;
                    ConnectSocket.Start(client);
                }
            }
            catch
            {
                MessageBox.Show("创建客户端连接失败！\n" + ListenSocket.RemoteEndPoint.ToString(), 
                    "创建连接出错", MessageBoxButtons.OK, MessageBoxIcon.Warning);

            }
        }

        public string GetRandomString(int length, bool useNum, bool useLow, bool useUpp, bool useSpe, string custom) //生成随机的密码salt
        {
            byte[] b = new byte[4];
            new System.Security.Cryptography.RNGCryptoServiceProvider().GetBytes(b);
            Random r = new Random(BitConverter.ToInt32(b, 0));
            string s = null, str = custom;
            if (useNum == true) { str += "0123456789"; }
            if (useLow == true) { str += "abcdefghijklmnopqrstuvwxyz"; }
            if (useUpp == true) { str += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; }
            if (useSpe == true) { str += "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; }
            for (int i = 0; i < length; i++)
            {
                s += str.Substring(r.Next(0, str.Length - 1), 1);
            }
            return s;
        }

        void ReceiveCommand(object obj)
        {
            ClientHandle client = obj as ClientHandle;
            Socket ReceiveCommandSocket = client.CommandSocket;
            Socket DataTransportSocket = client.DataSocket;

            /*
             *  开始处理登录认证过程
             */

            byte[] ReceivedBuffer = new byte[serverConfig.GetPocketBufferLength()];
            int DataPocketLength = ReceiveCommandSocket.Receive(ReceivedBuffer);
            ReadOnlySpan<byte> ReceiveBufferSpan = new ReadOnlySpan<byte>(ReceivedBuffer, 0, DataPocketLength);
            DataPocket LoginDataPocket = JsonSerializer.Deserialize<DataPocket>(ReceiveBufferSpan);
            if (LoginDataPocket.PocketType == 11)
            {
                String SqlConnent = @"server = ***; user = ***; database = ***; port = ***; password = ***";
                MySqlConnection sqlConnection = new MySqlConnection(SqlConnent);
                sqlConnection.Open();

                ReadOnlySpan<byte> FTPUserSpan = new ReadOnlySpan<byte>(LoginDataPocket.Content);
                FTPUser user = JsonSerializer.Deserialize<FTPUser>(FTPUserSpan);

                String SqlSelectStr = "select * from user where username=@ftp_username "; //SQL参数化以防止SQL注入攻击
                //SqlParameter UsernameValue = new SqlParameter("@ftp_username", user.User); 
                MySqlCommand mySqlCommand = new MySqlCommand(SqlSelectStr, sqlConnection);
                mySqlCommand.Parameters.AddWithValue("@ftp_username", user.User); //SQL参数化以防止SQL注入攻击
                MySqlDataReader reader = mySqlCommand.ExecuteReader();

                if(reader.HasRows == true)
                {
                    reader.Read();
                    LoginDataPocket.PocketType = 12; //将包改为登录响应包返回询问随机加盐sha256后的密码
                    user.Salt = GetRandomString(8, true, true, true, true, ""); //生成随机字符串发给客户端
                    Boolean isVIP = reader.GetInt32(reader.GetOrdinal("isVIP")) == 1 ? true : false;
                    user.IsVIP = isVIP;
                    LoginDataPocket.Content = JsonSerializer.SerializeToUtf8Bytes(user);
                    byte[] authSendBuffer = JsonSerializer.SerializeToUtf8Bytes(LoginDataPocket);
                    ReceiveCommandSocket.Send(authSendBuffer);

                    //认证密码，实际对比的是sha256(sha256(password)+随机salt)是否一致
                    String PasswordSHA256 = reader.GetString(reader.GetOrdinal("passwordHASH"));
                    byte[] SaltPasswordSHA256 = new SHA256Managed().ComputeHash(Encoding.UTF8.GetBytes(PasswordSHA256 + user.Salt));
                    String SaltPasswordSHA256Str = BitConverter.ToString(SaltPasswordSHA256).Replace("-", "").ToLower();
                    byte[] ReceivedAuthBuffer = new byte[serverConfig.GetPocketBufferLength()];
                    int AuthDataPocketLength = ReceiveCommandSocket.Receive(ReceivedAuthBuffer);
                    ReadOnlySpan<byte> ReceiveAuthBufferSpan = new ReadOnlySpan<byte>(ReceivedAuthBuffer, 0, AuthDataPocketLength);
                    DataPocket LoginAuthDataPocket = JsonSerializer.Deserialize<DataPocket>(ReceiveAuthBufferSpan);
                    ReadOnlySpan<byte> authUserSpan = new ReadOnlySpan<byte>(LoginAuthDataPocket.Content);
                    FTPUser authUser = JsonSerializer.Deserialize<FTPUser>(authUserSpan);
                    if (authUser.PasswordSHA256.Equals(SaltPasswordSHA256Str))
                    {
                        //返回认证成功包，可以继续此线程
                        DataPocket LoginSuccessPocket = new DataPocket();
                        LoginSuccessPocket.PocketType = 13; //13表示登陆认证成功返回包
                        byte[] SuccessSendBuffer = JsonSerializer.SerializeToUtf8Bytes(LoginSuccessPocket);
                        ReceiveCommandSocket.Send(SuccessSendBuffer);

                        //查询数据库若MetaDataPocket字段不为空说明上次传输中断
                        //读取数据库中MetaDataPocket等现场信息
                    }
                    else
                    {
                        //认证失败，密码错误，关闭连接
                        DataPocket LoginErrorPocket = new DataPocket();
                        LoginErrorPocket.PocketType = 14; //14表示登陆失败返回包
                        LoginErrorPocket.Content = Encoding.UTF8.GetBytes("密码错误!");
                        byte[] ErrorSendBuffer = JsonSerializer.SerializeToUtf8Bytes(LoginErrorPocket);
                        ReceiveCommandSocket.Send(ErrorSendBuffer);
                        ReceiveCommandSocket.Close();
                        DataTransportSocket.Close();
                        return;
                    }

                }
                else
                {
                    //返回用户名错误包并关闭连接
                    DataPocket LoginErrorPocket = new DataPocket();
                    LoginErrorPocket.PocketType = 14; //14表示登陆失败返回包
                    LoginErrorPocket.Content = Encoding.UTF8.GetBytes("用户不存在!");
                    byte[] ErrorSendBuffer = JsonSerializer.SerializeToUtf8Bytes(LoginErrorPocket);
                    ReceiveCommandSocket.Send(ErrorSendBuffer);
                    ReceiveCommandSocket.Close();
                    DataTransportSocket.Close();
                    return;
                }
            }
            else
            {
                //对于不发登录认证包的一律断开不处理，防止DDOS攻击
                ReceiveCommandSocket.Close();
                DataTransportSocket.Close();
                return;
            }


            try
            {
                while (true)
                {
                    //接收控制数据包
                    byte[] ReceiveBuffer = new byte[serverConfig.GetPocketBufferLength()];
                    int PocketLength = ReceiveCommandSocket.Receive(ReceiveBuffer);
                    if (PocketLength == 0)
                    {
                        Console.WriteLine(ReceiveCommandSocket.RemoteEndPoint.ToString() + "断开控制连接!");
                        ReceiveCommandSocket.Close();
                        DataTransportSocket.Close();
                        return;
                    }
                    //String ReceivedPocket = Encoding.UTF8.GetString(ReceiveBuffer, 0, PocketLength);
                    //DataPocket ReceivedDataPocket = JsonConvert.DeserializeObject<DataPocket>(ReceivedPocket);
                    ReadOnlySpan<byte> ReceivedBufferSpan = new ReadOnlySpan<byte>(ReceiveBuffer, 0, PocketLength);
                    DataPocket ReceivedDataPocket = JsonSerializer.Deserialize<DataPocket>(ReceivedBufferSpan);
                    label2.Text = ReceivedDataPocket.ToString();

                    //处理控制数据包指令
                    if (ReceivedDataPocket.PocketType == 21)
                    {
                        //if(ReceivedDataPocket.PocketDataLength == 0 && Encoding.UTF8.GetString(ReceivedDataPocket.Content).Equals("~"))
                        //{
                        String RequiredDirectory = Encoding.UTF8.GetString(ReceivedDataPocket.Content);
                        DataPocket ReturnDirectoryPocket = new DataPocket();
                        if (RequiredDirectory.Equals("~"))
                        {
                            ReturnDirectoryPocket.Content = Encoding.UTF8.GetBytes(serverConfig.GetServerDirection());
                        }
                        RequiredDirectory = RequiredDirectory.Equals("~") ? serverConfig.GetServerDirection() : RequiredDirectory;
                        FileSystemInfo[] DirectoryList = new DirectoryInfo(RequiredDirectory).GetFileSystemInfos();
                        ReturnDirectoryPocket.PocketType = 22;
                        ReturnDirectoryPocket.FileAndDirectoryCount = DirectoryList.Length;
                        
                        foreach (FileSystemInfo info in DirectoryList)
                        {
                            FileOrDirectory fileOrDirectory = new FileOrDirectory();
                            fileOrDirectory.Name = info.Name;
                            fileOrDirectory.FullName = info.FullName;
                            fileOrDirectory.LastWriteTime = info.LastWriteTime;
                            if (info is DirectoryInfo)
                            {
                                fileOrDirectory.FileOrDirectoryType = 1;
                            }
                            else
                            {
                                fileOrDirectory.FileOrDirectoryType = 0;
                                fileOrDirectory.Length = ((FileInfo)info).Length;
                            }
                            ReturnDirectoryPocket.FileAndDirectoryList.Add(fileOrDirectory);

                        }
                        //ReturnDirectoryPocket.FileAndDirectoryList = DirectoryList;
                        //byte[] SendBuffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(ReturnDirectoryPocket));
                        //ReceiveCommandSocket.Send(SendBuffer);
                        Console.WriteLine(ReturnDirectoryPocket.ToString());
                        byte[] SendBuffer = JsonSerializer.SerializeToUtf8Bytes(ReturnDirectoryPocket);
                        ReceiveCommandSocket.Send(SendBuffer);
                        //}
                    }

                    else if (ReceivedDataPocket.PocketType == 31)
                    {
                        //回复文件元数据包
                        DataPocket MetaData = new DataPocket();
                        MetaData.PocketType = 32; //32表示是client请求下载的文件元数据包
                        for (int i = 0; i < ReceivedDataPocket.FileAndDirectoryCount; i++)
                        {
                            String FileFullName = ReceivedDataPocket.FileAndDirectoryList[i].FullName;
                            FileInfo file = new FileInfo(FileFullName);
                            FileOrDirectory ClientDownloadFile = new FileOrDirectory();
                            ClientDownloadFile.FullName = file.FullName;
                            ClientDownloadFile.Name = file.Name;
                            ClientDownloadFile.Length = file.Length;
                            ClientDownloadFile.FileOrDirectoryType = 0;
                            ClientDownloadFile.LastWriteTime = file.LastWriteTime;
                            MetaData.FileAndDirectoryList.Add(ClientDownloadFile);
                            MetaData.FileAndDirectoryCount++;
                        }
                        byte[] SendBuffer = JsonSerializer.SerializeToUtf8Bytes(MetaData);
                        ReceiveCommandSocket.Send(SendBuffer);

                        //开启上传文件到客户端线程
                        client.MetaDataPocket = MetaData;
                        Thread TransportThread = new Thread(UploadForClientDownload);
                        TransportThread.IsBackground = true;
                        TransportThread.Start(client);

                    }
                    
                    else if(ReceivedDataPocket.PocketType == 41)
                    {
                        // 确认并同意上传请求
                        DataPocket EnsureUpload = new DataPocket();
                        EnsureUpload.PocketType = 42;
                        byte[] SendBuffer = JsonSerializer.SerializeToUtf8Bytes(EnsureUpload);
                        ReceiveCommandSocket.Send(SendBuffer);

                        //开启接收客户端上传文件线程
                        client.MetaDataPocket = ReceivedDataPocket;
                        Thread TransportThread = new Thread(DownloadForClientUpload);
                        TransportThread.IsBackground = true;
                        TransportThread.Start(client);
                    }
                }
            }
            catch
            {
                //MessageBox.Show("处理客户端控制指令出错，请检查网络与系统设置！\n" + ReceiveCommandSocket.RemoteEndPoint.ToString(),
                //    "处理出错", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Console.WriteLine(ReceiveCommandSocket.RemoteEndPoint.ToString() + "客户端断开连接!");
            }
        }

        private void Form2_FormClosing(object sender, FormClosingEventArgs e)
        {
            Process.GetCurrentProcess().Kill();
        }

        private void UploadForClientDownload(object obj)
        {
            ClientHandle client = obj as ClientHandle;
            Socket DataSocket = client.DataSocket;
            DataPocket MetaData = client.MetaDataPocket;
            for(int i=0; i<MetaData.FileAndDirectoryCount; i++)
            {
                FileStream fileStream = new FileStream(MetaData.FileAndDirectoryList[i].FullName, FileMode.Open, FileAccess.Read);
                BinaryReader reader = new BinaryReader(fileStream);
                for(int length=0; length<(MetaData.FileAndDirectoryList[i].Length + 1023) / 1024; length++)
                {
                    try
                    {
                        byte[] fileContent = reader.ReadBytes(1024);
                        DataPocket FileContentPocket = new DataPocket();
                        FileContentPocket.Content = fileContent;
                        FileContentPocket.PocketType = 33; //33表示此包为文件内容包
                        long RemainLength = MetaData.FileAndDirectoryList[i].Length - length * 1024;
                        FileContentPocket.PocketLength = (int)(RemainLength > 1024 ? 1024 : RemainLength);
                        byte[] SendBuffer = JsonSerializer.SerializeToUtf8Bytes(FileContentPocket);
                        DataSocket.Send(SendBuffer);

                        //等待确认此包后再发送下一个包
                        byte[] ReceiveBuffer = new byte[serverConfig.GetPocketBufferLength()];
                        int ReceiveLength = DataSocket.Receive(ReceiveBuffer);
                        ReadOnlySpan<byte> ReceiveBufferSpan = new ReadOnlySpan<byte>(ReceiveBuffer, 0, ReceiveLength);
                        DataPocket EnsurePocket = JsonSerializer.Deserialize<DataPocket>(ReceiveBufferSpan);
                        if (EnsurePocket.PocketType == 34) //34为数据包的确认包
                        {
                            continue;
                        }
                        else
                        {
                            Console.WriteLine("客户端下载数据传输出现问题，文件为：" + MetaData.FileAndDirectoryList[i].FullName + "。\n传输到第" + length.ToString() + "个文件内容包。");
                        }
                    }
                    catch
                    {
                        //说明传输中断，将信息存入数据库以便断点续传
                        //将文件元数据包存入数据库保存现场，以及正在传输的文件号i，传输到第几块length
                    }


                }
                reader.Close();
                fileStream.Close();
            }
        }

        private void DownloadForClientUpload(object obj)
        {
            ClientHandle helper = obj as ClientHandle;
            Socket DataSocket = helper.DataSocket;
            DataPocket MetaPocket = helper.MetaDataPocket;
            String DownloadDirectory = Encoding.UTF8.GetString(MetaPocket.Content);
            for(int i=0; i<MetaPocket.FileAndDirectoryCount; i++)
            {
                FileStream DownloadFile = new FileStream(DownloadDirectory + '/' + MetaPocket.FileAndDirectoryList[i].Name, FileMode.Create, FileAccess.Write);
                BinaryWriter FileBinaryWriter = new BinaryWriter(DownloadFile);
                for(int length=0; length<(MetaPocket.FileAndDirectoryList[i].Length + 1023) / 1024; length++)
                {
                    byte[] ReceiveBuffer = new byte[serverConfig.GetPocketBufferLength()];
                    int DataPocketLength =  DataSocket.Receive(ReceiveBuffer);
                    ReadOnlySpan<byte> ReceiveBufferSpan = new ReadOnlySpan<byte>(ReceiveBuffer, 0, DataPocketLength);
                    DataPocket DownloadData = JsonSerializer.Deserialize<DataPocket>(ReceiveBufferSpan);
                    FileBinaryWriter.Write(DownloadData.Content);

                    DataPocket EnsurePocket = new DataPocket();
                    EnsurePocket.PocketType = 34;
                    byte[] SendBuffer = JsonSerializer.SerializeToUtf8Bytes(EnsurePocket);
                    DataSocket.Send(SendBuffer);
                }
                FileBinaryWriter.Close();
                DownloadFile.Close();
            }
        }

    }
}
